<?php
namespace App\Http\Middleware;

use App\Exception\ServiceException;
use App\Helper\ServiceHelper;
use App\Lib\Consul\ServiceInfo;
use App\Lib\Consul\ServiceSelector;
use App\Lib\Consul\Service;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Swoft\Bean\Annotation\Mapping\Bean;
use Swoft\Bean\Annotation\Mapping\Inject;
use Swoft\Http\Server\Contract\MiddlewareInterface;
use Swoft\Validator\Annotation\Mapping\Validate;
use App\Http\Validator\ServiceValidator;

/**
 * 发现服务
 * Class ServicesMiddleware
 * @package App\Http\Middleware
 * @Bean("ServicesMiddleware")
 */
class ServicesMiddleware implements MiddlewareInterface
{


    /**
     * @Inject()
     * @var ServiceSelector
     */
    protected $ServiceSelector;

    /**
     * @Inject()
     * @var ServiceHelper
     */
    protected $ServiceHelper;

    /**
     * @param ServerRequestInterface $request
     * @param RequestHandlerInterface $handler
     * @return ResponseInterface
     * @throws ServiceException
     * @throws \Swoft\Consul\Exception\ClientException
     * @throws \Swoft\Consul\Exception\ServerException
     * @throws \Swoft\Validator\Exception\ValidatorException
     */
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        // 获取所有类型的数据
        $requestData = $request->input();

        // 检测 service 参数 参数格式为： Prefix.ServiceName.Server.Action
        \validate($requestData, "ServiceValidator", ['service']);
        // 初始化服务信息
        $serviceInfo = new ServiceInfo($requestData['service'] ?? '');

        // 检测服务是否符合接口规范
        if(!$serviceInfo->useful()){
            throw new ServiceException("Service 参数校验失败或不存在！");
        }

        //如果是本地进行consul寻找健康服务 ，其他环境直接走dns
        if(env('APP_ENV', 'local') === 'local') {
            // 使用consul 发现健康的服务列表
            /** @var ServiceSelector $ServiceSelector */
            $services = $this->ServiceSelector->services($serviceInfo->getServiceName());

            // 使用随机算法获取一个服务
            $service = $this->ServiceHelper->roundRobin($services);
        }else{
            $service['Meta']['SERVICE_ADDRESS'] = getSocketDns($serviceInfo->getServiceName());
            $service['Meta']['SERVICE_PORT'] = config('service.rpc_service_port', 18307);
            $service = new Service($service);
        }

        // 没有获取到服务 返回异常
        if(empty($service->getData())){
            throw new ServiceException("Service 不存在 3！");
        }
        // 检测发现的服务是否可用 除了健康 还需要具有必备的 Address、Port 参数
        if(! $service->useful()){
            throw new ServiceException("Service 不存在 2！");
        }

        //把服务信息传递给下一级
        return $handler->handle($request
            ->withAttribute('host', $service->getHost())
            ->withAttribute('serviceInfo', $serviceInfo)
            ->withAttribute('service', $service)
        );
    }
}
